const crypto = require('crypto')

// nodejs8没有base64url这种encoding，此处为对应的polyfill

function toBase64Url(str) {
	return str.replace(/=/g, '')
		.replace(/\+/g, '-')
		.replace(/\//g, '_')
}

function fromBase64Url(base64url) {
	base64url = base64url.toString()

	var padding = 4 - base64url.length % 4
	if (padding !== 4) {
		for (var i = 0; i < padding; ++i) {
			base64url += '='
		}
	}
	return base64url
		.replace(/-/g, '+')
		.replace(/_/g, '/')
}

function atob(str) {
	return Buffer.from(str, 'base64').toString('utf-8')
}

function btoa(str) {
	return Buffer.from(str, 'utf-8').toString('base64')
}

function fromToken(base64url) {
	return JSON.parse(
		atob(
			fromBase64Url(
				base64url
			)
		)
	)
}

function toToken(obj) {
	return toBase64Url(
		btoa(
			JSON.stringify(
				obj
			)
		)
	)
}

function getSign(str, secret) {
	return toBase64Url(
		crypto.createHmac('sha256', secret).update(str).digest('base64')
	)
}

const jwt = {
	verify: function(token, secret) {
		if (typeof token !== 'string') {
			throw new Error('Invalid token')
		}
		const tokenArr = token.split('.')
		if (tokenArr.length !== 3) {
			throw new Error('Invalid token')
		}
		const [headerBase64, payloadBase64, signatureBase64] = tokenArr
		if (getSign(headerBase64 + '.' + payloadBase64, secret) !== signatureBase64) {
			throw new Error('Invalid token')
		}
		const header = fromToken(headerBase64)
		// if (header.alg !== 'HS256' || header.typ !== 'JWT') {
		// 	throw new Error('Invalid token')
		// }
		if (header.alg !== 'HS256' || header.sign_type !== 'SIGN') {
			throw new Error('Invalid token')
		}
		const payload = fromToken(payloadBase64)

		if (payload.exp * 1000 < Date.now()) {
			const err = new Error('Token expired')
			err.name = 'TokenExpiredError'
			throw err
		}
		return payload
	},
	sign: function(data, secret, options = {}) {
		const {
			expiresIn
		} = options
		if (!expiresIn) {
			throw new Error('expiresIn is required')
		}
		// const header = {
		// 	alg: 'HS256',
		// 	typ: 'JWT'
		// }
		const header = {
			alg: 'HS256',
			sign_type: 'SIGN'
		}
		const now = parseInt(Date.now() / 1000)
		const payload = {
			...data,
			exp: now + options.expiresIn,
			timestamp: now,
		}
		const signStr = toToken(header) + '.' + toToken(payload)
		const signature = getSign(signStr, secret)
		return signStr + '.' + signature
	}
}

module.exports = {
  jwt
};